package com.ecarx.vhaltests;

import static org.junit.Assert.assertNotNull;

import android.car.Car;
import android.car.hardware.CarPropertyValue;
import android.car.hardware.property.CarPropertyManager;
import android.content.Context;
import android.util.Log;

import androidx.test.platform.app.InstrumentationRegistry;

import com.ecarx.xui.adaptapi.car.IWrapper;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;


public abstract class VhalUnitTestBase2 {

    public final String TAG = getClass().getSimpleName() + "TAG";

    public static final String TYPE_VALUE_BOOLEAN = "boolean";
    public static final String TYPE_VALUE_INT = "int";
    public static final String TYPE_VALUE_FLOAT = "float";

    private static final int STATUS_AVAILABLE = 0;
    private static final int STATUS_NOT_AVAILABLE = 1;

    private static final IWrapper wrapper;
    private final static Object lock = new Object();
    private final static IpcpClient ipcpClient = new IpcpClient();

    private static final CarPropertyManager mCarPropertyMgr;
    private static final Car mCar;
    public static int DELAY_GETTING_TIME = 100;

    private static Object mCallbackValue;
    private static int mCallbackStatus;


    @Before
    public void setup() {
        Log.d(TAG, "setup: ");
        init();
    }

    @After
    public void teardown() {
        release();
        Log.d(TAG, "teardown: ");
    }

    protected abstract int getPropertyId();

    protected abstract int getArea();

    protected abstract String getValueType();

    /**
     * 回调值
     */
    public Object getCallbackValue() {
        return mCallbackValue;
    }

    /**
     * 回调状态
     */
    public int getCallbackStatus() {
        return mCallbackStatus;
    }

    public IWrapper getWrapper() {
        return wrapper;
    }

    public static IpcpClient getIpcpClient() {
        return ipcpClient;
    }

    public CarPropertyManager getCarPropertyMgr() {
        return mCarPropertyMgr;
    }

    public Car getCar() {
        return mCar;
    }

    static {
        Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        mCar = Car.createCar(mContext);
        assertNotNull("Car instance is null", mCar);
        mCarPropertyMgr = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
        assertNotNull("CarPropertyMgr is null", mCarPropertyMgr);
        wrapper = com.ecarx.xui.adaptapi.car.Car.createWrapper(mContext);
    }

    public void init() {

        mCarPropertyMgr.registerCallback(carPropertyEventCallback, getPropertyId(), CarPropertyManager.SENSOR_RATE_ONCHANGE);
    }

    public void release() {
        mCarPropertyMgr.unregisterCallback(carPropertyEventCallback);
    }

    private final CarPropertyManager.CarPropertyEventCallback carPropertyEventCallback = new CarPropertyManager.CarPropertyEventCallback() {
        @Override
        public void onChangeEvent(CarPropertyValue carPropertyValue) {
            int areaId = carPropertyValue.getAreaId();
            if (areaId == getArea()) {
                mCallbackValue = carPropertyValue.getValue();
                mCallbackStatus = carPropertyValue.getStatus();

            }
            Log.d(TAG, "onChangeEvent:areaId = " + areaId + " mCallbackStatus = " + mCallbackStatus + " mCallbackValue = " + mCallbackValue);
            synchronized (lock) {
                lock.notify();
            }
        }

        @Override
        public void onErrorEvent(int i, int i1) {
            Log.d(TAG, "onErrorEvent: ");
        }
    };

    public void execTestAvailable(boolean isAvailable) {
        Log.d(TAG, "execTestAvailable: ");
        //等待回调
        lockWait();
        //验证回调
//        Assert.assertEquals(getCallbackStatus(), isAvailable ? STATUS_AVAILABLE : STATUS_NOT_AVAILABLE);
        boolean propertyAvailable = getCarPropertyMgr().isPropertyAvailable(getPropertyId(), getArea());
        Assert.assertEquals(isAvailable, propertyAvailable);
    }

    public void execTestAvailable(boolean isAvailable,boolean activeStatus) {
        Log.d(TAG, "execTestAvailable: ");
        //等待回调
        lockWait();
        //验证回调
//        Assert.assertEquals(getCallbackStatus(), activeStatus ? STATUS_AVAILABLE : STATUS_NOT_AVAILABLE);

        boolean propertyAvailable = getCarPropertyMgr().isPropertyAvailable(getPropertyId(), getArea());
        Assert.assertEquals(isAvailable, propertyAvailable);
    }

    public void execTestAvailable(boolean isAvailable,int area) {
        Log.d(TAG, "execTestAvailable: ");
        //等待回调
        lockWait();
        //验证回调
//        Assert.assertEquals(getCallbackStatus(), isAvailable ? STATUS_AVAILABLE : STATUS_NOT_AVAILABLE);
        boolean propertyAvailable = getCarPropertyMgr().isPropertyAvailable(getPropertyId(), area);
        Assert.assertEquals(isAvailable, propertyAvailable);
    }

    private void execTestValue(Object value, int area,Object targetValue,boolean verifyCallback) {
        String valueType = getValueType();
        Log.d(TAG, "execTestValue value:"+value+" valueType:"+valueType+" area :"+area+" targetValue:"+targetValue);
        //调用set方法
        switch (valueType) {
            case TYPE_VALUE_BOOLEAN:
                getCarPropertyMgr().setBooleanProperty(getPropertyId(), area, (Boolean) value);
                break;
            case TYPE_VALUE_FLOAT:
                getCarPropertyMgr().setFloatProperty(getPropertyId(), area, (Float) value);
                break;
            case TYPE_VALUE_INT:
                getCarPropertyMgr().setIntProperty(getPropertyId(), area, (Integer) value);
                break;
            default:
                break;
        }

        //等待回调
        lockWait();
        //验证回调
        if(verifyCallback){
            Assert.assertEquals(targetValue, getCallbackValue());
//            Assert.assertEquals(getCallbackStatus(), STATUS_AVAILABLE);
        }
        //验证get方法
        Object ret = null;
        switch (getValueType()) {
            case TYPE_VALUE_BOOLEAN:
                ret = getCarPropertyMgr().getBooleanProperty(getPropertyId(), area);
                break;
            case TYPE_VALUE_FLOAT:
                ret = getCarPropertyMgr().getFloatProperty(getPropertyId(), area);
                break;
            case TYPE_VALUE_INT:
                ret = getCarPropertyMgr().getIntProperty(getPropertyId(), area);
                break;
            default:
                break;
        }
        Assert.assertEquals(targetValue, ret);
    }

    public void execTestValue(Object value) {
        execTestValue(value, getArea(), value,true);
    }

    public void execTestValue(Object value,int area,boolean verifyCallback) {
        execTestValue(value,area,value,verifyCallback);
    }

    public void execTestValue(Object value, Object targetValue) {
        execTestValue(value, getArea(), value,true);
    }

    public void lockWait() {
        try {
            synchronized (lock) {
                lock.wait(1000);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void lockNotify() {
        synchronized (lock) {
            lock.notify();
        }
    }


    public void delay() {
        try {
            Thread.sleep(DELAY_GETTING_TIME);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void delay(int time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}